<?php

App::Import('Vendor', 'infofile');

/**
 * ftpman 的数据源。通过这个类可以访问 ftpman 所管理的仓库文件夹，可以获取文
 * 件夹和内容的基本属性，以及存储在特定信息文件中的额外属性。
 *
 * 关于仓库结构的具体内容，请参考：
 *
 *  http://code.google.com/p/ftpmanagesite/wiki/RepositoryStructure
 * 
 */
class FtpmanSource extends DataSource
{
    /* ftpman 所管理的文件夹的根目录 */
    var $rootdir = '';

    /* ftpman 所管理的文件夹访问时的 URL */
    var $rooturl = '';

    /* 文件名所使用的字符集 */
    var $charset = 'UTF-8';

    /* 存储文件夹额外信息的文件名 */
    var $dirinfo = '.dirinfo';

    /* 存储文件额外信息的文件名 */
    var $fileinfo = '.fileinfo';
	
	/* 存储用户收藏的文件夹名 */
	var $user_dir = 'users';
    
    /* 存储图标的文件夹名 */
    var $icon_dir = 'icons';

    /**
     * 构造函数。
     */
    function __construct($config) {
        parent::__construct($config);

        $this->rootdir = $this->config['rootdir'];
        $this->rooturl = $this->config['rooturl'];

        if (isset($this->config['charset'])) {
            $this->charset = $this->config['charset'];
        }
        if (isset($this->config['dirinfo'])) {
            $this->dirinfo = $this->config['dirinfo'];
        }
        if (isset($this->config['fileinfo'])) {
            $this->fileinfo = $this->config['fileinfo'];
        }
        if (isset($this->config['icon_dir'])) {
            $this->icon_dir = $this->config['icon_dir'];
        }
    }

    function __realpath($path) {
        // 检查 $path 文件是否在 $rootdir 中
        $fullpath = realpath($this->rootdir.DS.$path);
        if (!($fullpath && strpos($fullpath, $this->rootdir) === 0)) {
            throw new Exception("Path $fullpath is not under FTP root directory.");
        }
        return $fullpath;
    }

    /**
     * 给出路径，获取相应的文件项对象。
     */
    function get($path) {
        $fullpath = $this->__realpath($path);

        return new FtpEntry($this, $fullpath);
    }

    /**
     * 返回某路径的下载地址。
     */
    function url($path) {
        return $this->rooturl.$path;
	}
	
	/**
     * 给出用户名，获取相应的用户对象。
     */
	function user($name) {
		if($name == '')
			return false;
		else
			return new UserCollect($name, $this->user_dir, $this);
	}
		
	
}

class FtpEntry {

    private $datasource = null;
    private $path = null;
    private $__fileinfo = null;
    private $stat = null;
    private $modified = array();
    private $icon_dir = 'icons';
    
    public $filename = null;
    public $basename = null;
    public $b64filename = null;

    static $default_values = array(
        'Description' => '', 'CommentGrade' => null,
        'DownloadCount' => 0, 'Author' => null,
        'Sticky' => null, 'Promote' => null, 'Order' => null,
        'View' => null,
    );

    /**
     * 构造函数。
     *
     * @param $datasource 相关的数据源，可以获取配置信息。
     * @param $path 文件的绝对路径。
     */
    function __construct($datasource, $path) {
        $this->datasource = $datasource;
        $this->path = $path;
        $this->stat = stat($this->path);
        $this->__fileinfo = null;

        $this->basename = basename($this->path);
        $this->filename = substr($this->path, strlen($this->datasource->rootdir));
        $this->b64filename = base64_encode($this->filename);
    }

    /**
     * 析构函数。保存未保存的信息。
     */
    function __destruct() {
        if (!empty($this->modified)) {
            $this->save_fileinfo();
        }
    }

    /**
     * 获取私有属性的值。此函数对外部提供文件的各种信息，包括存储在信息文件
     * 中的信息和 stat 获取的文件属性。
     */
    function __get($name) {
        $fileinfo = $this->get_fileinfo();

        if (method_exists($this, "get$name")) {
            return call_user_func(array($this, "get$name"));
        }

        // 返回属性的值
        if (array_key_exists($name, $fileinfo)) {
            return $fileinfo[$name];
        }

        // 对于标准的属性，若尚未赋值则返回缺省值
        if (array_key_exists($name, self::$default_values)) {
            return self::$default_values[$name];
        }

        // 返回文件信息的值
        if (array_key_exists($name, $this->stat)) {
            return $this->stat[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    /**
     * 修改文件项某个属性的值。
     */
    function set($name, $value) {
        $this->modified[$name] = $value;  
    }

    /**
     * 处理文件显示名
     */
    function getName() {
        $fileinfo = $this->get_fileinfo();
        if (array_key_exists('Name', $fileinfo))
            return $fileinfo['Name'];
        else
            return $this->basename;
    }

    /**
     * 处理文件大小，如果是叶子目录，则返回目录的总大小
     */
    function getSize() {
        if ($this->is_dir() && $this->is_leaf())
            return $this->du_s();
        else
            return $this->stat['size'];
    }

    function get_parent_entry() {
        $ppath = dirname($this->filename);
        if ($ppath == '') return null;

        return $this->datasource->get($ppath);
    }

    /**
     * 获取文件项的信息。
     */
    function get_fileinfo() {
        if ($this->__fileinfo == null) {
            if ($this->is_dir()) {
                $filename = $this->path.DS.$this->datasource->dirinfo;
            } else {
                $filename = dirname($this->path).DS.$this->datasource->fileinfo;
            }
            $info = new InfoFile($filename);
            $info = $info->load();

            if ($this->is_dir()) {
                if (array_key_exists('__DIR__', $info))
                    $this->__fileinfo = $info['__DIR__'];
            } else {
                if (array_key_exists($this->basename, $info))
                    $this->__fileinfo = $info[$this->basename];
            }

            if ($this->__fileinfo == null) $this->__fileinfo = array();
        }
        return array_merge($this->__fileinfo, $this->modified);
    }

    /**
     * 保存文件项信息。
     */
    function save_fileinfo() {
		if (!empty($this->modified)) {
            if ($this->is_dir()) {
                $filename = $this->path.DS.$this->datasource->dirinfo;
            } else {
                $filename = dirname($this->path).DS.$this->datasource->fileinfo;
            }
            $infofile = new InfoFile($filename);
            $info = $infofile->load();
			print_r($info);
            if ($this->is_dir()) {
                $info['__DIR__'] = $this->get_fileinfo();
            } else {
                $info[$this->basename] = $this->get_fileinfo();
            }
            $infofile->save($info);
            $this->__fileinfo = null;
            $this->modified = array();
        }
    }

    /**
     * 判断当前项是否是叶子项。
     */
    function is_leaf() {
        if (!$this->is_dir()) return true;

        $fileinfo = $this->get_fileinfo();

        if (array_key_exists('Leaf', $fileinfo) &&
            strtolower($fileinfo['Leaf']) == 'false')
        {
            return false; 
        }

        $d = dir($this->path);
        while (false !== ($entry = $d->read())) {
            if (substr($entry , 0, 1) == '.') continue;
            $entrypath = $this->path.DS.$entry;
            if (is_dir($entrypath)) return false;
        }

        return true;
    }

    /**
     * 判断当前项是否是目录。
     */
    function is_dir() {
        return is_dir($this->path);
    }

    /**
     * 获取图标文件的路径。
     */
    function get_icon_path() {
        $fileinfo = $this->get_fileinfo();
        /*如果用户设定了图标路径*/
        if(array_key_exists('Icon_path', $fileinfo)){
            if(file_exists($fileinfo['Icon_path'])){
                return $fileinfo['Icon_path'];
            }
        }
        
        /*如果用户设定了图标的文件名*/
        if (array_key_exists('Icon', $fileinfo)){
            /*如果在该目录下存在用户设定的图标*/
            if(file_exists($this->path.DS.$fileinfo['Icon'])) {           
                return $this->path.DS.$fileinfo['Icon'];
            } else if(file_exists($this->icon_dir.DS.$fileinfo['Icon'])) {
                return $this->icon_dir.DS.$fileinfo['Icon'];
            }
        } 
        
        /*先搜索该目录下有无图标文件，没有则使用我们给定的默认图标*/
        $dir_icon = $this->__dir_icon();
        if ($this->is_dir() == true && $dir_icon != ''){
            /*如果在该目录下存在.icon.***图片类型的文件*/
            return $this->path.DS.$dir_icon;
        } else { 
            /*获取默认图标*/
            $default = $this->__default_icon();
            return $default;
        }
    }
    
    /**
     * 根据文件类型或者文件夹返回不同的默认图标
     */
    function __default_icon() {
        if($this->is_dir())
            return IMAGES.$this->icon_dir.DS.'DefaultDirIcon.png';
        $default = '';
        $ext = substr($this->filename, strrpos($this->filename, '.'));
        switch ($ext) {
            case '.rar':
                $default = 'DefaultRarIcon.png';
            break;
            case '.zip':
                $default = 'DefaultZipIcon.png';
            break;
            
            case '.jpg':
            case '.jpeg':
                $default = 'DefaultJpegIcon.png';
            break;
            case '.png':
                $default = 'DefaultPngIcon.png';
            break;
            case '.ico':
            case '.gif':
                $default = 'DefaultPicIcon.png';
            break;
            
            case '.doc':
                $default = 'DefaultDocIcon.png';
            break;
            case '.ppt':
                $default = 'DefaultPptIcon.png';
            break;
            case '.xls':
                $default = 'DefaultXlsIcon.png';
            break; 
            case '.txt':
                $default = 'DefaultTxtIcon.png';
            break; 
            case '.pdf':
                $default = 'DefaultPdfIcon.png';
            break; 
            case '.chm':
                $default = 'DefaultChmIcon.png';
            break; 
            case '.html':
            case '.htm':
                $default = 'DefaultHtmlIcon.png';
            break;
            case '.url':
                $default = 'DefaultUrlIcon.png';
            break;
            
            case '.aif':
                $default = 'DefaultAifIcon.png';
            break; 
            case '.wav':
                $default = 'DefaultWavIcon.png';
            break;
            case '.wma':
                $default = 'DefaultWmaIcon.png';
            break;
            case '.mp3':
                $default = 'DefaultMp3Icon.png';
            break;
            case '.mid':
                $default = 'DefaultMidIcon.png';
            break;
            
            case '.avi':
                $default = 'DefaultAviIcon.png';
            break;
            case '.mov':
                $default = 'DefaultMovIcon.png';
            break;
            case '.mpeg':
                $default = 'DefaultMpegIcon.png';
            break;
            case '.mpg':
                $default = 'DefaultMpgIcon.png';
            break;
            case '.wmv':
                $default = 'DefaultWmvIcon.png';
            break;
            
            case '.dll':
                $default = 'DefaultDllIcon.png';
            break;
            case '.hlp':
                $default = 'DefaultHlpIcon.png';
            break;
            case '.inf':
                $default = 'DefaultInfIcon.png';
            break;
            case '.reg':
                $default = 'DefaultRegIcon.png';
            break;
            case '.rtf':
                $default = 'DefaultRtfIcon.png';
            break;
            case '.tif':
                $default = 'DefaultTifIcon.png';
            break;
            case '.vbs':
                $default = 'DefaultVbsIcon.png';
            break;
            default:
                $default = 'DefaultIcon.png';
        }
        return IMAGES.DS.$this->icon_dir.DS.$default;
    }
    
    /**
     * 查询该目录下有没有.icon.***图形文件
     */
    function __dir_icon() {
        if($this->is_dir() == false)
            return '';
        $d = dir($this->path);
        $icon_path = '';
        while (false !== ($entry = $d->read())) {
            /*按优先级png,ico,gif,jpg查找图标文件*/
            if (preg_match('/^.icon.(png|ico|gif|jpg|bmp)$/',$entry)) {
                $icon_path = $entry;
                break;
            }
        }
        $d->close();
        return $icon_path;
    }

    /**
     * 列目录。
     */
    function ls() {
        $ret = array('dirs' => array(), 'files' => array());

        $d = dir($this->path);
        while (false !== ($entry = $d->read())) {
            if (substr($entry, 0, 1) == '.') continue;

            $entrypath = $this->filename.DS.$entry;
            try {
                $entryobj = $this->datasource->get($entrypath);
            } catch (Exception $e) {
                // 如果子目录不在 FTP 目录中，则跳过
                continue;
            }
            if ($entryobj->is_leaf()) {
                $ret['files'][$entryobj->basename] = $entryobj;
            } else {
                $ret['dirs'][$entryobj->basename] = $entryobj;
            }
        }
        $d->close();

        uasort($ret['files'], array($this, 'ls_sort_default'));
        uasort($ret['dirs'], array($this, 'ls_sort_default'));

        return $ret;
    }

    /**
     * 默认的为文件排序的函数
     */
    function ls_sort_default($a, $b) {
        $asticky = $a->Sticky === null ? 10000 : (int) $a->Sticky;
        $bsticky = $b->Sticky === null ? 10000 : (int) $b->Sticky;
        if ($asticky != $bsticky) {
            return $asticky - $bsticky;
        }

        $orders = $this->__get('Order');
        if ($orders !== null) {
            $aidx = strpos($orders, $a->basename);
            if ($aidx === false) $aidx = 10000;
            $bidx = strpos($orders, $b->basename);
            if ($bidx === false) $bidx = 10000;
            if ($aidx != $bidx) 
                return $aidx - $bidx;
        }
        
        return strcmp($a->basename, $b->basename);
    }

    /**
     * 删除叶子项。
     */
    function unlink() {
        if ($this->is_dir() && !$this->is_leaf()) {
            throw new Exception("目录中还有其它内容");
        }
        
        if ($this->is_dir()) {
            $d = dir($this->path);
            while (false !== ($entry = $d->read())) {
                if ($entry == '.' || $entry == '..') continue;
                if (!@unlink($this->path.DS.$entry))
                    throw new Exception(@$php_errormsg);
            }
            $d->close();
            if (!@rmdir($this->path))
                throw new Exception(@$php_errormsg);
       	} else {
           	if (!@unlink($this->path))
                throw new Exception(@$php_errormsg);
        }
    }

    /**
     * 获取文件项占用的磁盘空间的总大小。
     */
    function du_s() {
        $obj = new FtpEntryDuS();
        $this->_do_recursive($obj);
        return $obj->ret;
    }

    /**
     * 返回目录中所有文件的列表。列表项包括文件的相对文件名和绝对路径。
     */
    function find() {
        if ($this->is_dir()) {
            $obj = new FtpEntryFind($this->path);
            $this->_do_recursive($obj);
            return $obj->ret;
        } else {
            return array(basename($this->path) => $this->path);
        }
    }

    /**
     * 以递归的方式执行某个动作。
     *
     * @param $func 要执行的函数
     * @param $p 执行路径，如果是 null 则使用当前类的路径
     */
    function _do_recursive($func, $p = null) {
        if ($p == null) $p = $this->path;

        if (is_object($func))
            $func->__invoke($p);
        else
            $func($p);

        if (is_dir($p)) {
            $d = dir($p);
            while(false !== ($entry = $d->read())) {
                if ($entry == '.' || $entry == '..') continue;
                $entrypath = $p.DS.$entry;
                $this->_do_recursive($func, $entrypath);
            }
            $d->close();
        }
    }

}

/**
 * 用来计算文件项所占磁盘空间的类。配合 FtpEntry 的 do_s() 函数使用。
 */
class FtpEntryDuS {
    public $ret = 0;

    function __invoke($p) {
        $s = stat($p);
        $this->ret += $s['size'];
    }
}

/**
 * 在一个目录下递归查找全部文件。
 *
 * 此函数的最终返回值为一个数组，数组项的键为相对路径，值为绝对路径。
 */
class FtpEntryFind {
    public $ret = array();
    
    /**
     * 构造函数。
     *
     * @param $rootpath 跟目录的路径，用来计算结果中的相对路径。
     */
    function __construct($rootpath) {
        $this->rootpath = $rootpath;
        $this->rplen = strlen($this->rootpath) + 1;
    }

    function __invoke($p) {
        $relativepath = substr($p, $this->rplen);
        if ($relativepath) 
            $this->ret[$relativepath] = $p;
    }
	
}


/**
 * 用户收藏的操作。
 */
class UserCollect {
	
	private $username = null;
    private $userdir = null;
	private $datasource = null;
	private $users = null;			//保存用户收藏文件夹的目录路径
    

    /**
     * 构造函数。
     *
     * @param $username 用户名。
     * @param $userdir 存放用户收藏的文件夹。
	 *  用户路径出现问题，需要测试修改,已解决
     */
    function __construct($username, $userdir, $datasouce) {
        $this->username = $username;
		$this->users = $datasouce->rootdir.DS.$userdir;
        $this->userdir =$this->users.DS.$username;
		$this->datasouce = $datasouce;
		$this->__check();
    }

    /**
     * 析构函数。
     */
    function __destruct() {
    }
	
	/**
	 * 检查用户的目录是否存在，不存在就新建目录
	 */
	function __check() {
		//如果没有users文件夹先建立一个
		if(!is_dir($this->users))
			mkdir($this->users, 0777);
		
		if(!is_dir($this->userdir))
			if(!mkdir($this->userdir, 0700)){
				echo "create ".$this->userdir." error!";
				return false;
			}
		return true;
		
	}
	
	/**
	 * 列出用户的收藏，返回文件的信息
	 * 暂时将文件夹和文件的信息放在一起，供测试使用
	 */
	function ls() {
		$ret = array();

        $d = dir($this->userdir);
        while (false !== ($entry = $d->read())) {
            if (substr($entry, 0, 1) == '.') continue;

            $entrypath = $this->userdir.DS.$entry;
            try {
                $entryobj = new FtpEntry($this->datasouce, $entrypath);
            } catch (Exception $e) {
                // 如果子目录不在 FTP 目录中，则跳过
                continue;
            }
			$ret[$entryobj->basename] = $entryobj;
            /*if (!is_dir($entrypath)) {
                $ret['files'][$entryobj->basename] = $entryobj;
            } else {
                $ret['dirs'][$entryobj->basename] = $entryobj;
            }*/
        }
        $d->close();

        sort($ret);
        //sort($ret['dirs']);

        return $ret;
	}
	
	/**
	 * 删除用户收藏的文件
	 * @param $path 用户想删除的文件路径
	 */
	function del($path) {
		$ppath = $this->datasouce->rootdir.$path;
		if(is_dir($ppath)) {
			if (!@rmdir($ppath))
                throw new Exception(@$php_errormsg);
		}else if (!@unlink($ppath))
            throw new Exception(@$php_errormsg);
	}
	
	/**
	 * 增加用户收藏
	 * @param $path 用户想增加的文件路径
	 */
	function add($path) {
		$file = basename($path);
		$target = $this->userdir.$file;
		$link = $this->datasouce->rootdir.$path;
		if(!link($target, $link))
			throw new Exception(@$php_errormsg);
	}
			
			
}

?>
